using Crux.Core.Dapper.Attributes;

namespace Crux.Core.Dapper.SqlGenerator;


public partial class SqlGenerator<TEntity>
    where TEntity : class
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    /// <param name="softDelete"></param>
    /// <returns></returns>
    public virtual SqlQuery GetDelete(TEntity entity, bool softDelete = true)
    {
        var sqlQuery = new SqlQuery(this.Provider);
        var whereAndSql =
            string.Join(" AND ", KeySqlProperties.Select(p => string.Format("{0}.{1} = {2}", TableName, p.ColumnName, ParameterSymbol + p.PropertyName)));

        if (!LogicalDelete || !softDelete)
        {
            sqlQuery.SqlBuilder
                .Append("DELETE FROM ")
                .Append(TableName)
                .Append(" WHERE ")
                .Append(whereAndSql);
        }
        else
        {
            sqlQuery.SqlBuilder
                .Append("UPDATE ")
                .Append(TableName)
                .Append(" SET ")
                .Append(StatusPropertyName)
                .Append(" = ")
                .Append(LogicalDeleteValue);

            if (HasUpdatedAt && UpdatedAtProperty.GetCustomAttribute<UpdatedAtAttribute>() is { } attribute)
            {
                var offset = attribute.TimeKind == DateTimeKind.Local
                    ? new DateTimeOffset(DateTime.Now)
                    : new DateTimeOffset(DateTime.UtcNow);
                if (attribute.OffSet != 0)
                {
                    offset = offset.ToOffset(TimeSpan.FromHours(attribute.OffSet));
                }

                if (typeof(System.DateTimeOffset) == UpdatedAtProperty!.PropertyType)
                {
                    UpdatedAtProperty.SetValue(entity, offset);
                }
                else
                {
                    UpdatedAtProperty.SetValue(entity, offset.DateTime);
                }

                sqlQuery.SqlBuilder
                    .Append(", ")
                    .Append(UpdatedAtPropertyMetadata!.ColumnName)
                    .Append($" = {ParameterSymbol}")
                    .Append(UpdatedAtPropertyMetadata.PropertyName);
            }

            sqlQuery.SqlBuilder
                .Append(" WHERE ")
                .Append(whereAndSql);
        }

        sqlQuery.SetParam(entity);
        return sqlQuery;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="predicate"></param>
    /// <param name="softDelete"></param>
    /// <returns></returns>
    public virtual SqlQuery GetDelete(Expression<Func<TEntity, bool>>? predicate, bool softDelete = true)
    {
        var sqlQuery = new SqlQuery(this.Provider);

        if (!LogicalDelete || !softDelete)
        {
            sqlQuery.SqlBuilder
                .Append("DELETE FROM ")
                .Append(TableName);
        }
        else
        {
            sqlQuery.SqlBuilder
                .Append("UPDATE ")
                .Append(TableName)
                .Append(" SET ")
                .Append(StatusPropertyName)
                .Append(" = ")
                .Append(LogicalDeleteValue);

            if (HasUpdatedAt)
                sqlQuery.SqlBuilder
                    .Append(", ")
                    .Append(UpdatedAtPropertyMetadata!.ColumnName)
                    .Append($" = {ParameterSymbol}")
                    .Append(UpdatedAtPropertyMetadata.PropertyName);
        }

        sqlQuery.SqlBuilder.Append(" ");
        AppendWherePredicateQuery(sqlQuery, predicate, QueryType.Delete);
        return sqlQuery;
    }

}